using System;
using System.Xml.Serialization;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using FarseerGames.FarseerPhysics.Collisions;
using FarseerGames.FarseerPhysics.Dynamics;

namespace ProjectNoClue
{
	/// <summary>
	/// Basic element for ships
	/// </summary>
	[Serializable]
	public class Part : ICloneable
	{
		/// <summary>
		/// Trasformation matrix for relative to absolute
		/// </summary>
		Matrix translation = Matrix.Identity;
		[XmlIgnore]
		public Matrix Translation
		{
			get
			{
				if (body != null)
				{
					Position = body.Position;
					Rotation = body.Rotation;
				}
				return translation;
			}

		}

		Body body;
		[XmlIgnore]
		public Body Body { get { return body; } internal set { body = value; } }

		bool updated = false;

		internal Vector2 position = Vector2.Zero;
		/// <summary>
		/// Position of part
		/// </summary>
		public Vector2 Position
		{
			get { return (body != null) ? body.Position : position; }
			set
			{
				if (body != null)
					body.Position = value;

				position = value;
				translation.M41 = position.X;
				translation.M42 = position.Y;

				updated = false;
			}
		}

		internal float rotation = 0;
		/// <summary>
		/// Rotation(in radients)
		/// </summary>
		public float Rotation
		{
			get { return (body != null) ? body.Rotation : rotation; }
			set
			{
				if (body != null)
					body.Rotation = value;

				rotation = value;
				//Rotation along the Z axis
				float cos = (float)Math.Cos(rotation);
				float sin = (float)Math.Sin(rotation);
				translation.M11 = cos;
				translation.M12 = sin;
				translation.M21 = -sin;
				translation.M22 = cos;

				updated = false;
			}
		}



		/// <summary>
		/// Array of convex sub-parts
		/// </summary>
		public Shape[] Shapes;

		/// <summary>
		/// Updates all AbsoluteVertices
		/// </summary>
		public unsafe void UpdateAbsolute()
		{
			Shape shp;

			if (!updated)
			{
				for (int ii = 0; ii < Shapes.Length; ++ii)
				{
					shp = Shapes[ii];
					if (shp.AbsoluteVertices == null)
						shp.AbsoluteVertices = new Vector2[shp.Vertices.Length];

					fixed (Vector2* verArr = shp.Vertices)
					{
						Vector2* verPtr = verArr;
						fixed (Vector2* absArr = shp.AbsoluteVertices)
						{
							Vector2* absPtr = absArr;

							for (int jj = 0; jj < shp.Vertices.Length; ++jj, ++verPtr, ++absPtr)
							{
								#region INLINE: *absPtr = Vector2.Transform(*verPtr, Translation);

								absPtr->X = ((verPtr->X * translation.M11) + (verPtr->Y * translation.M21)) + translation.M41;
								absPtr->Y = ((verPtr->X * translation.M12) + (verPtr->Y * translation.M22)) + translation.M42;

								#endregion
							}
						}
					}

				}

				updated = true;
			}
		}

		/// <summary>
		/// Creates all Edges in the shapes
		/// </summary>
		public void CreateEdges()
		{
			if (Shapes[0].Edges != null) //Edges already created
				return;

			Shape shp;
			Vector2 last;
			Vector2 tmp;
			Vector2[] verts;
			Shape.Edge[] edges;
			int[] hidden;

			for (int ii = 0; ii < Shapes.Length; ++ii)
			{
				shp = Shapes[ii];
				if (shp.HiddenEdges == null)
					shp.HiddenEdges = new int[0];
				hidden = shp.HiddenEdges;
				verts = shp.Vertices;
				edges = shp.Edges = new Shape.Edge[verts.Length - hidden.Length];
				Array.Sort<int>(hidden);

				int kk = 0;
				int hidx = 0;
				last = verts[0];
				for (int jj = 1; jj < verts.Length; ++jj)
				{
					if (hidden.Length > hidx && hidden[hidx] == (jj - 1))
					{
						++hidx;
						last = verts[jj];
						continue;
					}

					tmp = verts[jj] - last;
					double angle = Math.Atan2(tmp.Y, tmp.X);

					edges[kk].Point = last;
					edges[kk].LenghtSinCos = new Vector3(tmp.Length(), (float)Math.Sin(angle), (float)Math.Cos(angle));

					++kk;
					last = verts[jj];
				}
				if (!(hidden.Length > hidx && hidden[hidx] == verts.Length - 1) && kk < edges.Length)
				{
					tmp = verts[0] - last;
					float angle = (float)Math.Atan2(tmp.Y, tmp.X);

					edges[kk].Point = last;
					edges[kk].LenghtSinCos = new Vector3(tmp.Length(), (float)Math.Sin(angle), (float)Math.Cos(angle));
				}

			}
		}

		/// <summary>
		/// Convex sub-parts
		/// </summary>
		[Serializable]
		public class Shape
		{
			/// <summary>
			/// Vertices relative to the part's center
			/// </summary>
			public Vector2[] Vertices;

			Geom geom;
			[XmlIgnore]
			public Geom Geom { get { return geom; } internal set { geom = value; } }

			/// <summary>
			/// Vertecies tranformed to screen space
			/// </summary>
			[XmlIgnore]
			public Vector2[] AbsoluteVertices = null;

			/// <summary>
			/// Color of the lines in the shape
			/// </summary>
			public Color ShapeColor = Color.White;

			/// <summary>
			/// Which edge will be hidden when the shape is drawn
			/// </summary>
			/// <remarks>only used during the creation of Edges</remarks>
			public int[] HiddenEdges;

			/// <summary>
			/// Edges to be drawn
			/// </summary>
			[XmlIgnore]
			public Edge[] Edges = null;

			/// <summary>
			/// Edge declaration
			/// </summary>
			public struct Edge
			{
				public Vector2 Point;
				public Vector3 LenghtSinCos;
			}
		}

		#region ICloneable Members

		/// <summary>
		/// Almost deep copy
		/// </summary>
		/// <returns>new Part copy</returns>
		/// <remarks>Since the Vertices don't change, no need to copy them</remarks>
		public object Clone()
		{
			Part res = new Part();

			res.translation = this.translation;
			res.position = this.position;
			res.rotation = this.rotation;

			res.updated = false;

			res.Shapes = new Shape[this.Shapes.Length];

			Shape nshp;
			Shape oshp;
			for (int ii = 0; ii < this.Shapes.Length; ++ii)
			{
				nshp = res.Shapes[ii] = new Shape();
				oshp = Shapes[ii];

				nshp.ShapeColor = oshp.ShapeColor;
				nshp.HiddenEdges = oshp.HiddenEdges;
				nshp.Vertices = oshp.Vertices;
				nshp.Edges = oshp.Edges;

				//Uncomment the folling region if deepcopy is required...
				#region DeepCopy for Shapes

				/*	nshp.ShapeColor = oshp.ShapeColor;

				nshp.HiddenEdges = new int[oshp.HiddenEdges.Length];
				for (int jj = 0; jj < oshp.HiddenEdges.Length; ++jj)
				{
					nshp.HiddenEdges[ii] = oshp.HiddenEdges[ii];
				}

				nshp.Vertices = new Vector2[oshp.Vertices.Length];
				for (int jj = 0; jj < oshp.Vertices.Length; ++jj)
				{
					nshp.Vertices[ii] = oshp.Vertices[ii];
				}

				nshp.Edges = new Shape.Edge[oshp.Edges.Length];
				for (int jj = 0; jj < oshp.Edges.Length; ++jj)
				{
					nshp.Edges[ii] = oshp.Edges[ii];
				}	*/
				#endregion

			}


			return res;
		}

		#endregion
	}
}